home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Games / Xconq 7.1.0 / src / xconq-7.1.0 / x11 / xshowimf.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-07  |  41.8 KB  |  1,770 lines  |  [TEXT/R*ch]

  1. /* A no-longer simple X program to display and edit xconq image families.
  2.    Copyright (C) 1994, 1995, 1996 Massimo Campostrini & Stanley T. Shebs.
  3.  
  4. Xconq is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.  See the file COPYING.  */
  8.  
  9. /* note: display of three-color images (mono+mask) is controlled 
  10.    by the resource "maskColor" (see XShowimf-color.ad) or by the 
  11.    command-line argument "-mc" */
  12.  
  13. #include <stdio.h>
  14. #include <X11/Intrinsic.h>
  15. #include <X11/Xresource.h>
  16. #include <X11/StringDefs.h>
  17. #include <X11/Shell.h>
  18. #include <X11/Xaw/Form.h>
  19. #include <X11/Xaw/Box.h>
  20. #include <X11/Xaw/Label.h>
  21. #include <X11/Xaw/Command.h>
  22. #include <X11/Xaw/Toggle.h>
  23. #include <X11/Xaw/Viewport.h>
  24. #include <X11/cursorfont.h>
  25. #include <bitmaps/check.b>
  26.  
  27. #include "config.h"
  28. #include "misc.h"
  29. #include "lisp.h"
  30. #include "imf.h"
  31. #include "ximf.h"
  32. #include "xutil.h"
  33.  
  34. /* MAXROWS, MAXCOLS, MINROWS, and MINCOLS can be changed safely */
  35. #define MAXROWS 50
  36. #define MAXCOLS 16
  37. #define MINROWS 2
  38. #define MINCOLS 8
  39.  
  40. #define MAXFAMS (MAXROWS*MAXCOLS)
  41.  
  42. extern int numimages;
  43. int numfamilies;
  44. extern ImageFamily **images;
  45. extern char *outdirname;
  46.  
  47. /* the number of allowed magnifications NUMMAGN 
  48.    and the magnification scales magnif can be changed safely */
  49. #define NUMMAGN 4
  50. int magnif[NUMMAGN] = { 1, 2, 4, 6 };
  51.  
  52. typedef enum _prep {
  53.   prep_none,
  54.   prep_delete,
  55.   prep_update,
  56.   prep_export
  57. } Prep_type;
  58.  
  59. typedef struct a_image_stuff {
  60.   int w, h;
  61.   Widget mono_w, mask_w, comb_w, colr_w;
  62.   Pixmap mono_p[NUMMAGN], mask_p[NUMMAGN], comb_p[NUMMAGN], colr_p[NUMMAGN];
  63.   struct a_image_stuff *next;
  64. } ImageStuff;
  65.  
  66. typedef struct a_family_stuff {
  67.   Widget image, name;
  68.   Widget shell, form, label, increase_magn, decrease_magn, close,
  69.          delete, update, export;
  70.   int imagn, changed;
  71.   Prep_type prep;
  72.   ImageStuff *images;
  73. } FamilyStuff;
  74.  
  75. FamilyStuff family[MAXFAMS];
  76.  
  77. int basew=32, baseh=32;
  78.  
  79. int cols, rows, color_comb;
  80.  
  81. Arg tmpargs[10];
  82.  
  83. Widget toplevel;
  84. Widget main_form;
  85. Widget mainviewp;
  86. Widget viewform;
  87. Widget help;
  88. Widget help_shell = NULL;
  89. Widget select_widget;
  90. Widget deselect;
  91. Widget toggle;
  92. Widget read_button;
  93. Widget save_button;
  94. Widget delete;
  95. Widget quit_button;
  96. Widget message;
  97.  
  98. char buffer[200], shortbuf[100];
  99.  
  100. XtAppContext app_con;
  101.  
  102. Display *dpy;
  103. Window rootwin, win;
  104. XrmDatabase xrdb;
  105. Pixel mask_pixel;
  106. Colormap cmap;
  107. int depth, screen;
  108. XVisualInfo vinfo;
  109. Pixmap check;
  110. char *read_suggest="", *write_suggest="";
  111.  
  112. XrmOptionDescRec xoptions[] = {
  113.     { "-geometry",    "*geometry",    XrmoptionSepArg,    NULL },
  114.     { "-xrm",        NULL,        XrmoptionResArg,    NULL }
  115. };
  116.  
  117. static String fallback_resources[] = {
  118. "XShowimf*Command.Font:        -adobe-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*",
  119. "XShowimf*Label.Font:        -adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*",
  120. "XShowimf*Toggle.Font:        -adobe-helvetica-medium-r-*-*-10-*-*-*-*-*-*-*",
  121. "XShowimf*increase_magn.Label:    + magn",
  122. "XShowimf*decrease_magn.Label:    - magn",
  123. "XShowimf*mainViewport.height:    484",
  124. "XShowimf*mainForm.?.top:    ChainTop",
  125. "XShowimf*mainForm.?.bottom:    ChainTop",
  126. "XShowimf*mainForm.?.left:    ChainLeft",
  127. "XShowimf*mainForm.?.right:    ChainLeft",
  128. "XShowimf*mainForm.mainViewport.bottom:    ChainBottom",
  129. "XShowimf*mainForm.mainViewport.right:    ChainRight",
  130. "XShowimf*viewForm.?.top:    ChainTop",
  131. "XShowimf*viewForm.?.bottom:    ChainTop",
  132. "XShowimf*viewForm.?.left:    ChainLeft",
  133. "XShowimf*viewForm.?.right:    ChainLeft",
  134. "XShowimf*form.label.width:    310",
  135. "XShowimf*message.width:    365",
  136.  
  137. "XShowimf*helpText.font:    -adobe-times-medium-r-*-*-14-*-*-*-*-*-*-*",
  138. "XShowimf*help.title:        xshowimf help",
  139. "XShowimf*helpDone.Label:    done",
  140. "XShowimf*helpText.label:\
  141. \\    MAIN WINDOW\\n\
  142. help:  if you see this, you know what it does\\n\
  143. select:  select all families\\n\
  144. deselect:  deselect all families\\n\
  145. toggle:  toggle selection\\n\
  146. read:  read a imf/xbm/xpm file\\n\
  147. save:  save selected families to an imf file\\n\
  148. delete:  delete selected families\\n\
  149. quit:  terminate\\n\
  150.  \\n\
  151. family icons:  click to popup a close-up window\\n\
  152. family names:  click to toggle selection\\n\
  153.  \\n\
  154.     CLOSE-UP WINDOWS\\n\
  155. + magn:  increase mgnification\\n\
  156. - magn:  decrease mgnification\\n\
  157. delete:  delete a single image\\n\
  158. update:  re-read an image from disk\\n\
  159. export:  export an image to disk \\n\
  160. close:  pop down this window\\n\
  161.  \\n\
  162. There are no edit buttons (yet); to edit an\\n\
  163. image, export it to disk, edit it with your\\n\
  164. favorite paint program (in the directory\\n\
  165. selected by the \"-o\" option), and update.",
  166.  
  167. "selFile.selFileForm.Font:     -adobe-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*",
  168. "selFile.selFileForm.Label.Font:   -adobe-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*",
  169. "selFile.selFileForm.Command.Font: -adobe-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*",
  170. "selFile*Scrollbar.thickness:    6",
  171. "selFile*selFilePrompt.height:    30",
  172.   NULL
  173. };
  174.  
  175. void show_image   PARAMS ((Widget w, XEvent *event, String *params, Cardinal *num_params));
  176. void show_family  PARAMS ((Widget w, XEvent *event, String *params, Cardinal *num_params));
  177. void image_action PARAMS ((Widget w, XEvent *event, String *params, Cardinal *num_params));
  178.  
  179. static XtActionsRec  actions[] = {
  180.   { "ShowImage",   show_image   },       /* set info line for this image */
  181.   { "ShowFamily",  show_family  },       /* set info line for family */
  182.   { "ImageAction", image_action },       /* do image stuff */
  183. };
  184.  
  185. /* translation table for label widgets for individual images */
  186. static char Translations[] = 
  187.   "<EnterWindow>:  ShowImage() \n\
  188.    <LeaveWindow>:  ShowFamily() \n\
  189.    <BtnDown>:      ImageAction() ";
  190.  
  191. void do_help();
  192. void done_help();
  193. void do_select();
  194. void do_deselect();
  195. void do_toggle();
  196. void do_read();
  197. void do_save();
  198. void do_delete();
  199. void do_quit();
  200. void do_open_family();
  201. void do_prep_delete();
  202. void do_prep_update();
  203. void do_prep_export();
  204. void do_close_family();
  205. void do_increase_magn();
  206. void do_decrease_magn();
  207.  
  208. void show_image_families PARAMS ((int first_time));
  209. void hide_image_families PARAMS ((void));
  210.  
  211. void usage PARAMS ((void));
  212. void build_name PARAMS ((char *name, char *first, char *second));
  213. void display_family PARAMS ((int i, int imagn));
  214. void undisplay_family PARAMS ((int i));
  215. ImageStuff *init_ims PARAMS ((Image *img, Widget parent));
  216. int shell_index PARAMS ((Widget w));
  217. Pixmap magnify_bitmap PARAMS ((char *data, int h, int w, int s));
  218. Pixmap magnify_colrpix PARAMS ((Image *img, int s, Pixel bg_pix, Pixmap mask));
  219. void reset_prep PARAMS ((int i));
  220. void set_cursor PARAMS ((FamilyStuff *fms, Cursor cursor));
  221. int delete_image PARAMS ((Widget w));
  222. int update_or_export_image PARAMS ((Widget w, int flag));
  223. Image *find_image PARAMS ((ImageFamily *imf, int w, int h));
  224. void destroy_family PARAMS ((int i));
  225. void delete_family PARAMS ((int i));
  226. int empty_family PARAMS ((int i)); 
  227. void mark_changed PARAMS ((ImageFamily *imf, int dummy));
  228.  
  229. #ifdef HAVE_SELFILE
  230. extern FILE *XsraSelFile();
  231. #endif
  232.  
  233. int
  234. main(argc, argv)
  235. int argc;
  236. char *argv[];
  237. {
  238.     char *arg, *mask_color_name = NULL;
  239.     char *stype;
  240.     XrmValue val;
  241.     XColor color, junk;
  242.     int i, nfsave;
  243.     
  244.     for (i = 0; i < MAXFAMS; i++) {
  245.     family[i].shell = NULL;
  246.     family[i].changed = FALSE;
  247.     }
  248.     
  249.     init_lisp();
  250.     
  251.     /* toplevel X stuff */
  252.     toplevel =
  253.       XtAppInitialize(&app_con, "XShowimf",
  254.               xoptions, XtNumber(xoptions), &argc, argv,
  255.               fallback_resources, NULL, 0);
  256.     XtAppAddActions(app_con, actions, XtNumber(actions));
  257.     dpy = XtDisplay(toplevel);
  258.     rootwin = DefaultRootWindow(dpy);
  259.     screen = DefaultScreen(dpy);
  260.     
  261.     /* process non-X argument */
  262.     for (i = 1; i < argc; ++i) {
  263.     arg = argv[i];
  264.     if (arg && !strcmp(arg, "-mc")) {
  265.         if (i + 1 < argc) {
  266.         mask_color_name = xmalloc((2 + strlen(argv[i + 1])) * sizeof(char));
  267.         strcpy(mask_color_name, argv[i + 1]);
  268.         argv[i] = NULL;
  269.         argv[i + 1] = NULL;
  270.         } else {
  271.         low_init_error("No color following -mc");
  272.         usage();
  273.         }
  274.     } else if (arg && (!strcmp(arg, "-help") || !strcmp(arg, "--help"))) {
  275.         usage();
  276.     } else if (arg && !strcmp(arg, "-o")) {
  277.         if (i+1 < argc) {
  278.         outdirname = argv[i+1];
  279.         /* Blast the arg because we'll be scanning the args again
  280.            and we want to ignore it then. */
  281.         argv[i] = NULL;
  282.         argv[i+1] = NULL;
  283.         ++i;
  284.         } else {
  285.         low_init_error("No output directory following -o");
  286.         usage();
  287.         }
  288.     }
  289.     }
  290.     
  291.     /* read the files */
  292.     for (i = 1; i < argc; ++i) {
  293.     if (argv[i] != NULL) {
  294.         read_suggest = write_suggest = argv[i];
  295.         /* try to guess the format and read the file */
  296.         if (!read_any_file(argv[i], NULL)) {
  297.         run_warning("Couldn't read \"%s\"", argv[i]);
  298.         }
  299.     }
  300.     }
  301.     
  302.     main_form =
  303.       XtVaCreateManagedWidget("mainForm", formWidgetClass, toplevel,
  304.                   NULL);
  305.     
  306.     help =
  307.       XtVaCreateManagedWidget("help", commandWidgetClass, main_form,
  308.                   NULL);
  309.     XtAddCallback (help, XtNcallback, do_help, NULL);
  310.     
  311.     select_widget =
  312.       XtVaCreateManagedWidget("select", commandWidgetClass, main_form,
  313.                   XtNfromHoriz, help,
  314.                   NULL);
  315.     XtAddCallback (select_widget, XtNcallback, do_select, NULL);
  316.     
  317.     deselect =
  318.       XtVaCreateManagedWidget("deselect", commandWidgetClass, main_form,
  319.                   XtNfromHoriz, select_widget,
  320.                   NULL);
  321.     XtAddCallback (deselect, XtNcallback, do_deselect, NULL);
  322.     
  323.     toggle =
  324.       XtVaCreateManagedWidget("toggle", commandWidgetClass, main_form,
  325.                   XtNfromHoriz, deselect,
  326.                   NULL);
  327.     XtAddCallback (toggle, XtNcallback, do_toggle, NULL);
  328.     
  329.     read_button =
  330.       XtVaCreateManagedWidget("read", commandWidgetClass, main_form,
  331.                   XtNfromHoriz, toggle,
  332.                   NULL);
  333.     XtAddCallback (read_button, XtNcallback, do_read, NULL);
  334.     
  335.     save_button =
  336.       XtVaCreateManagedWidget("save", commandWidgetClass, main_form,
  337.                   XtNfromHoriz, read_button,
  338.                   NULL);
  339.     XtAddCallback (save_button, XtNcallback, do_save, NULL);
  340.     
  341.     delete =
  342.       XtVaCreateManagedWidget("delete", commandWidgetClass, main_form,
  343.                   XtNfromHoriz, save_button,
  344.                   NULL);
  345.     XtAddCallback (delete, XtNcallback, do_delete, NULL);
  346.     
  347.     quit_button =
  348.       XtVaCreateManagedWidget("quit", commandWidgetClass, main_form,
  349.                   XtNfromHoriz, delete,
  350.                   NULL);
  351.     XtAddCallback (quit_button, XtNcallback, do_quit, NULL);
  352.     
  353.     message =
  354.       XtVaCreateManagedWidget("message", labelWidgetClass, main_form,
  355.                   XtNfromVert, help,
  356.                   NULL);
  357.     
  358.     nfsave = numfamilies = numimages;
  359.     if (numfamilies>MAXFAMS) {
  360.     numfamilies = MAXFAMS;
  361.     }
  362.     
  363.     mainviewp = XtVaCreateManagedWidget("mainViewport", viewportWidgetClass,
  364.                     main_form,
  365.                     XtNfromVert,   message,
  366.                     XtNallowVert,  True,
  367.                     NULL);
  368.     cols = MINCOLS;
  369.     show_image_families(TRUE);
  370.     
  371.     XtRealizeWidget(toplevel);
  372.     
  373.     /* several of the following functions work only
  374.        after XtRealizeWidget has been called */
  375.     
  376.     sprintf(buffer, "%d image families found", numfamilies);
  377.     if (nfsave>MAXFAMS) {
  378.     sprintf(buffer, "%d image families found, but I can display only %d",
  379.         nfsave, MAXFAMS);
  380.     }
  381.     XtVaSetValues(message, XtNlabel, buffer, NULL);
  382.     
  383.     xrdb = XtDatabase(dpy);
  384.     win = XtWindow(toplevel);
  385.     cmap = XDefaultColormap(dpy,screen);
  386.     depth = DefaultDepth(dpy,screen);
  387.     
  388.     if (XrmGetResource(xrdb, "xshowimf.maskColor",
  389.                "XShowimf.maskColor", &stype, &val)) {
  390.     if (strcmp(stype, "String")) {
  391.         fprintf(stderr, "resource type for maskColor is %s, ignoring\n", stype);
  392.     } else {
  393.         /* command line argument takes precedence over resource */
  394.         if (!mask_color_name) {
  395.         mask_color_name = val.addr;
  396.         }
  397.     }
  398.     }
  399.     
  400.     check = XCreateBitmapFromData(dpy, rootwin, 
  401.                   (char *) check_bits, check_width, check_height);
  402.     
  403.     /* only depths 4, 8, 16, 24, and 32 are supported */
  404.     if ((depth == 4 || depth % 8 == 0) && mask_color_name) {
  405.     color_comb = 1;
  406.     if (XAllocNamedColor(dpy, cmap, mask_color_name, &color, &junk)) {
  407.         mask_pixel = color.pixel;
  408.     } else {
  409.         mask_pixel = XBlackPixel(dpy, screen);
  410.     }
  411.     } else {
  412.     color_comb = 0;
  413.     }
  414.     
  415.     XtAppMainLoop (app_con);
  416.     
  417.     return 0;
  418. }
  419.  
  420. void
  421. show_image_families(first_time)
  422. int first_time;
  423. {
  424.     int i, a, needw, needh, vd, hd;
  425.     static int vert_distance, horiz_distance, maxrows;
  426.     Image *img;
  427.     X11Image *ximg;
  428.     XWindowChanges wchanges;
  429.     Dimension fw, fh, vh, dh, sw;
  430.     Widget dummy, vert;
  431.     static int oldrows = 0, oldcols = 0;
  432.     
  433.     /* pick a number of columns that gives a nice display */
  434.     for (; cols<=MAXCOLS; cols++) {
  435.     if (2*cols*cols>3*numfamilies && cols*MAXROWS>=numfamilies)  break;
  436.     }
  437.     rows = (numfamilies+cols-1)/cols;
  438.     rows = max(rows,MINROWS);
  439.     
  440.     if (first_time) {
  441.     /* 1st pass: create dummy widgets just to get values
  442.        and to estimated needed height and width */
  443.  
  444.     /* be sure we get a vertical scrollbar */
  445.     XtVaGetValues(mainviewp, XtNheight, &vh, NULL);
  446.     viewform =
  447.       XtVaCreateManagedWidget("viewForm", formWidgetClass, mainviewp,
  448.                   XtNheight, 2 * vh,
  449.                   NULL);
  450.     dummy =
  451.       XtVaCreateManagedWidget("dummy", commandWidgetClass, viewform,
  452.                   NULL);
  453.     vert = XtNameToWidget(mainviewp, "vertical");
  454.     if (vert) {
  455.         XtVaGetValues(vert,  XtNwidth, &sw,  NULL);
  456.     } else {
  457.         sw = 10;
  458.     }
  459.     XtVaGetValues(dummy,
  460.               XtNvertDistance, &vd,
  461.               XtNhorizDistance, &hd,
  462.               XtNheight, &dh,
  463.               NULL);
  464.     XtDestroyWidget(dummy);
  465.     vert_distance = baseh+4 + dh+1 + vd;
  466.     horiz_distance = basew+8 + 2+hd;
  467.     maxrows = vh/vert_distance;
  468.     needh = vert_distance*rows + vd;
  469.     needw = horiz_distance*cols + hd;
  470.     if (vh < 20)
  471.       vh = needh;
  472.     
  473.     XtDestroyWidget(viewform);
  474.     XtVaSetValues(mainviewp,
  475.               XtNheight, min(needh, vh),
  476.               XtNwidth, needw+sw+1,
  477.               NULL);
  478.  
  479.     /* 2nd pass: create the real widgets */
  480.  
  481.     viewform =
  482.       XtVaCreateManagedWidget("viewForm", formWidgetClass, mainviewp,
  483.                   XtNheight, needh,
  484.                   XtNwidth, needw+sw+1,
  485.                   NULL);
  486.     } else {
  487.     /* not first_time */
  488.     if ((rows != oldrows && (oldrows < maxrows || rows < maxrows))
  489.         || cols != oldcols) {
  490.         /* ask the window manager to change main window size */
  491.         XtVaGetValues(main_form,
  492.               XtNheight, &fh,
  493.               XtNwidth,  &fw,
  494.               NULL);
  495.         wchanges.width  = fw + (cols-oldcols)*horiz_distance;
  496.         wchanges.height =
  497.           fh + (min(rows,maxrows)-min(oldrows,maxrows))*vert_distance;
  498.         XReconfigureWMWindow(dpy, XtWindow(toplevel), screen, 
  499.                  CWWidth | CWHeight, &wchanges);
  500.     }
  501.     }
  502.     
  503.     oldrows = rows;
  504.     oldcols = cols;
  505.     
  506.     /* entries for each image family */
  507.     for (i = 0; i < numfamilies; i++) {
  508.  
  509.     /* picture */
  510.     a = 0;
  511.     XtSetArg(tmpargs[a], XtNwidth,  basew+8);  a++;
  512.     XtSetArg(tmpargs[a], XtNheight, baseh+4);  a++;
  513.     XtSetArg(tmpargs[a], XtNlabel, "");  a++;
  514.     x11_interp_imf(dpy, rootwin, images[i], TRUE);
  515.     img = best_image(images[i], basew, baseh);
  516.     if (img->hook != NULL) {
  517.         ximg = (X11Image *) img->hook;
  518.         if (ximg->colr != None) {
  519.         XtSetArg(tmpargs[a], XtNbitmap, ximg->colr);  a++;
  520.         } else if (ximg->mono != None) {
  521.         XtSetArg(tmpargs[a], XtNbitmap, ximg->mono);  a++;
  522.         } else if (ximg->mask != None) {
  523.         XtSetArg(tmpargs[a], XtNbitmap, ximg->mask);  a++;
  524.         }
  525.     }
  526.     if (i >= cols) {
  527.         XtSetArg(tmpargs[a], XtNfromVert,  family[i-cols].name);  a++;
  528.     }
  529.     if (i % cols) {
  530.         XtSetArg(tmpargs[a], XtNfromHoriz, family[i-1].image);  a++;
  531.     }
  532.  
  533.     build_name(buffer, "image_", images[i]->name);
  534.     family[i].image =
  535.       XtCreateManagedWidget(buffer, commandWidgetClass, viewform,
  536.                 tmpargs, a);
  537.     XtAddCallback (family[i].image, XtNcallback, do_open_family, NULL);
  538.  
  539.     /* name */
  540.     build_name(buffer, "name_", images[i]->name);
  541.     a = 0;
  542.     XtSetArg(tmpargs[a], XtNwidth, basew+8);  a++;
  543.     XtSetArg(tmpargs[a], XtNlabel, images[i]->name);  a++;
  544.     XtSetArg(tmpargs[a], XtNfromVert, family[i].image);  a++;
  545.     XtSetArg(tmpargs[a], XtNvertDistance, -1);  a++;
  546.     if (i % cols) {
  547.         XtSetArg(tmpargs[a], XtNfromHoriz, family[i-1].name);  a++;
  548.     }
  549.     family[i].name =
  550.       XtCreateManagedWidget(buffer, toggleWidgetClass, viewform,
  551.                 tmpargs, a);
  552.     }
  553. }
  554.  
  555. void
  556. hide_image_families()
  557. {
  558.     int i;
  559.  
  560.     /* entries for each image family */
  561.     for (i = 0; i < numfamilies; i++) {
  562.     if (family[i].image)
  563.       XtDestroyWidget(family[i].image);
  564.     if (family[i].name)
  565.       XtDestroyWidget(family[i].name);
  566.   }
  567. }
  568.  
  569. void
  570. usage()
  571. {
  572.     fprintf(stderr,
  573.       "usage: xshowimf [-mc mask color] [-o outdir] imfile ...\n");
  574.     exit(1);
  575. }
  576.  
  577. /* popup a window displaying all the images in the family */
  578.  
  579. void
  580. display_family(i, imagn)
  581. int i;
  582. int imagn;
  583. {
  584.     int img0;
  585.     Widget last, up;
  586.     Image *img;
  587.     FamilyStuff *fms = &family[i];
  588.     ImageStuff *ims;
  589.  
  590.     fms->imagn = imagn;
  591.  
  592.     build_name(buffer, "xshowimf_", images[i]->name);
  593.     fms->shell = XtVaCreatePopupShell(buffer, topLevelShellWidgetClass,
  594.                       toplevel, NULL);
  595.  
  596.     fms->form =
  597.       XtVaCreateManagedWidget("form", formWidgetClass, fms->shell,
  598.                   NULL);
  599.  
  600.     fms->increase_magn =
  601.       XtVaCreateManagedWidget("increase_magn", commandWidgetClass, fms->form, 
  602.                   NULL);
  603.     XtAddCallback (fms->increase_magn, XtNcallback, do_increase_magn, NULL);
  604.  
  605.     fms->decrease_magn =
  606.       XtVaCreateManagedWidget("decrease_magn", commandWidgetClass, fms->form, 
  607.                   XtNfromHoriz, fms->increase_magn,
  608.                   NULL);
  609.     XtAddCallback (fms->decrease_magn, XtNcallback, do_decrease_magn, NULL);
  610.  
  611.     fms->delete =
  612.       XtVaCreateManagedWidget("delete", commandWidgetClass, fms->form, 
  613.                   XtNfromHoriz, fms->decrease_magn,
  614.                   NULL);
  615.     XtAddCallback (fms->delete, XtNcallback, do_prep_delete, NULL);
  616.  
  617.     fms->update =
  618.       XtVaCreateManagedWidget("update", commandWidgetClass, fms->form, 
  619.                   XtNfromHoriz, fms->delete,
  620.                   NULL);
  621.     XtAddCallback (fms->update, XtNcallback, do_prep_update, NULL);
  622.  
  623.     fms->export =
  624.       XtVaCreateManagedWidget("export", commandWidgetClass, fms->form, 
  625.                   XtNfromHoriz, fms->update,
  626.                   NULL);
  627.     XtAddCallback (fms->export, XtNcallback, do_prep_export, NULL);
  628.  
  629.     fms->close =
  630.       XtVaCreateManagedWidget("close", commandWidgetClass, fms->form, 
  631.                   XtNfromHoriz, fms->export,
  632.                   NULL);
  633.     XtAddCallback (fms->close, XtNcallback, do_close_family, NULL);
  634.  
  635.     fms->label =
  636.       XtVaCreateManagedWidget("label", labelWidgetClass, fms->form,
  637.                   XtNfromVert, fms->increase_magn,
  638.                   NULL);
  639.  
  640.     last = fms->label;
  641.     img0 = 1;
  642.     for (img = images[i]->images; img != NULL; img = img->next) {
  643.     if (last)
  644.       up = last;
  645.     last = NULL;
  646.  
  647.     if (img->hook) {
  648.         if (img0) {
  649.         if (!fms->images || fms->changed) {
  650.             fms->images = init_ims(img, fms->form);
  651.         }
  652.         ims = fms->images;
  653.         img0 = 0;
  654.         } else {
  655.         if (!ims->next || fms->changed) {
  656.             ims->next = init_ims(img, fms->form);
  657.         }
  658.         ims = ims->next;
  659.         }
  660.         
  661.         if (ims->mono_p[imagn] != None) {
  662.         sprintf(buffer, "mono-%dx%d", img->w, img->h);
  663.         ims->mono_w = last =
  664.           XtVaCreateManagedWidget(buffer, labelWidgetClass, fms->form,
  665.                       XtNbitmap, ims->mono_p[imagn],
  666.                       XtNfromHoriz, last,
  667.                       XtNfromVert, up,
  668.                       NULL);
  669.         XtOverrideTranslations(ims->mono_w,
  670.                        XtParseTranslationTable(Translations));
  671.         } else {
  672.         ims->mono_w = NULL;
  673.         }
  674.         if (ims->mask_p[imagn] != None) {
  675.         sprintf(buffer, "mask-%dx%d", img->w, img->h);
  676.         ims->mask_w = last =
  677.           XtVaCreateManagedWidget(buffer, labelWidgetClass, fms->form,
  678.                       XtNbitmap, ims->mask_p[imagn],
  679.                       XtNfromHoriz, last,
  680.                       XtNfromVert, up,
  681.                       NULL);
  682.         XtOverrideTranslations(ims->mask_w,
  683.                        XtParseTranslationTable(Translations));
  684.         } else {
  685.         ims->mask_w = NULL;
  686.         }
  687.         if (ims->comb_p[imagn] != None) {
  688.         sprintf(buffer, "comb-%dx%d", img->w, img->h);
  689.         ims->comb_w = last =
  690.           XtVaCreateManagedWidget(buffer, labelWidgetClass, fms->form,
  691.                       XtNbitmap, ims->comb_p[imagn], 
  692.                       XtNfromHoriz, last,
  693.                       XtNfromVert, up,
  694.                       NULL);
  695.         XtOverrideTranslations(ims->comb_w,
  696.                        XtParseTranslationTable(Translations));
  697.         } else {
  698.         ims->comb_w = NULL;
  699.         }
  700.         if (ims->colr_p[imagn] != None) {
  701.         sprintf(buffer, "colr-%dx%d", img->w, img->h);
  702.         ims->colr_w = last =
  703.           XtVaCreateManagedWidget(buffer, labelWidgetClass, fms->form,
  704.                       XtNbitmap, ims->colr_p[imagn],
  705.                       XtNfromHoriz, last,
  706.                       XtNfromVert, up,
  707.                       NULL);
  708.         XtOverrideTranslations(ims->colr_w,
  709.                        XtParseTranslationTable(Translations));
  710.         } else {
  711.         ims->colr_w = NULL;
  712.         }
  713.     }
  714.     }
  715.     fms->changed = FALSE;
  716.  
  717.     /* now show them */
  718.     reset_prep(i);
  719.     XtPopup(fms->shell, XtGrabNone);
  720.     show_family(fms->label, NULL, NULL, NULL);
  721. }
  722.  
  723. void
  724. undisplay_family(i)
  725. int i;
  726. {
  727.     FamilyStuff *fms = &family[i];
  728.     ImageStuff *ims;
  729.  
  730.     if (!fms->shell) {
  731.     return;
  732.     }
  733.  
  734.     XtPopdown(fms->shell);
  735.  
  736.     for (ims = fms->images; ims; ims = ims->next) {
  737.     if (ims->mono_w) {
  738.         XtDestroyWidget(ims->mono_w);
  739.     }
  740.     if (ims->mask_w) {
  741.         XtDestroyWidget(ims->mask_w);
  742.     }
  743.     if (ims->comb_w) {
  744.         XtDestroyWidget(ims->comb_w);
  745.     }
  746.     if (ims->colr_w) {
  747.         XtDestroyWidget(ims->colr_w);
  748.     }
  749.     }
  750.  
  751.     XtDestroyWidget(fms->label);
  752.     XtDestroyWidget(fms->increase_magn);
  753.     XtDestroyWidget(fms->decrease_magn);
  754.     XtDestroyWidget(fms->close);
  755.     XtDestroyWidget(fms->delete);
  756.     XtDestroyWidget(fms->update);
  757.     XtDestroyWidget(fms->export);
  758.     XtDestroyWidget(fms->label);
  759.     XtDestroyWidget(fms->form);
  760.     XtDestroyWidget(fms->shell);
  761.  
  762.     fms->shell = NULL;
  763. }
  764.  
  765. ImageStuff *
  766. init_ims (img, parent)
  767. Image *img;
  768. Widget parent;
  769. {
  770.     int bytesize, j, w, h;
  771.     Pixmap pix;
  772.     X11Image *ximg;
  773.     GC gc;
  774.     ImageStuff *ims;
  775.     char *rawdata;
  776.     Pixel bg_pixel, fg_pixel;
  777.     Widget dummy;
  778.     
  779.     ximg = (X11Image *) img->hook;
  780.     if (!ximg)  return NULL;
  781.     
  782.     ims = (ImageStuff *) xmalloc(sizeof(ImageStuff));
  783.     ims->next = NULL;
  784.     for (j=0; j<NUMMAGN; j++) {
  785.     ims->mono_p[j] = None;
  786.     ims->mask_p[j] = None;
  787.     ims->comb_p[j] = None;
  788.     ims->colr_p[j] = None;
  789.     }
  790.     ims->h = img->h;
  791.     ims->w = img->w;
  792.     
  793.     /* build pixmaps */
  794.     if (ximg->mono != None) {
  795.     ims->mono_p[0] = ximg->mono;
  796.     if (img->rawmonodata) {
  797.         for (j=1; j<NUMMAGN; j++) {
  798.         ims->mono_p[j] =
  799.           magnify_bitmap(img->rawmonodata, img->h, img->w, magnif[j]);
  800.         }
  801.     }
  802.     }
  803.     
  804.     if (ximg->mask != None) {
  805.     ims->mask_p[0] = ximg->mask;
  806.     if (img->rawmaskdata) {
  807.         for (j=1; j<NUMMAGN; j++) {
  808.         ims->mask_p[j] =
  809.           magnify_bitmap(img->rawmaskdata, img->h, img->w, magnif[j]);
  810.         }
  811.     }
  812.     }
  813.     
  814.     if (ximg->mono != None && img->rawmonodata &&
  815.     ximg->mask != None && img->rawmaskdata) {
  816.     bytesize = ((img->w + 7) / 8) * img->h;
  817.     
  818.     if (!color_comb) {
  819.         /* monochrome */
  820.         rawdata = (char *) malloc(bytesize*sizeof(char));
  821.         if (rawdata) {
  822.         for (j=0; j<bytesize; j++) {
  823.             rawdata[j] = img->rawmonodata[j] | ~img->rawmaskdata[j];
  824.         }
  825.         pix = XCreateBitmapFromData(dpy, rootwin, rawdata, img->w, img->h);
  826.         ims->comb_p[0] = pix;
  827.         for (j=1; j<NUMMAGN; j++) {
  828.             ims->comb_p[j] = magnify_bitmap(rawdata, img->h, img->w, magnif[j]);
  829.         }
  830.         free(rawdata);
  831.         }
  832.  
  833.     } else {
  834.         /* color or grayscale */
  835.         /* get pixels; the widget has not been created yet, so we need a dummy */
  836.         sprintf(buffer, "comb-%dx%d", img->w, img->h);
  837.         dummy = XtVaCreateManagedWidget(buffer, labelWidgetClass, parent, NULL); 
  838.         XtVaGetValues(dummy,
  839.               XtNbackground, &bg_pixel,
  840.               XtNforeground, &fg_pixel,
  841.               NULL);
  842.         XtDestroyWidget(dummy);
  843.         
  844.         for (j=0; j<NUMMAGN; j++) {
  845.         w = img->w*magnif[j];
  846.         h = img->h*magnif[j];
  847.         pix = XCreatePixmap(dpy, rootwin, w, h, depth);
  848.         if (!pix || pix==None)  continue;
  849.         gc = XCreateGC(dpy, pix, 0, NULL);
  850.  
  851.         /* background */
  852.         XSetClipOrigin(dpy, gc, 0, 0);
  853.         XSetForeground(dpy, gc, bg_pixel);
  854.         XFillRectangle(dpy, pix, gc, 0, 0, w, h);
  855.  
  856.         /* mask */
  857.         XSetForeground(dpy, gc, mask_pixel);
  858.         XSetClipMask(dpy, gc, ims->mask_p[j]);
  859.         XFillRectangle(dpy, pix, gc, 0, 0, w, h);
  860.  
  861.         /* mono */
  862.         XSetForeground(dpy, gc, fg_pixel);
  863.         XSetClipMask(dpy, gc, ims->mono_p[j]);
  864.         XFillRectangle(dpy, pix, gc, 0, 0, w, h);
  865.  
  866.         ims->comb_p[j] = pix;
  867.         }
  868.     }
  869.     }
  870.     if (ximg->colr != None) {
  871.     /* get bg pixel; the widget has not been created yet, so we need a dummy */
  872.     sprintf(buffer, "colr-%dx%d", img->w, img->h);
  873.     dummy =
  874.       XtVaCreateManagedWidget(buffer, labelWidgetClass, parent, NULL); 
  875.     XtVaGetValues(dummy,
  876.               XtNbackground, &bg_pixel,
  877.               NULL);
  878.     XtDestroyWidget(dummy);
  879.  
  880.     if (img->rawcolrdata) {
  881.         for (j = 0; j < NUMMAGN; j++) {
  882.         ims->colr_p[j] =
  883.           magnify_colrpix(img, magnif[j], bg_pixel, ims->mask_p[j]);
  884.         }
  885.     }
  886.     }
  887.     
  888.     return ims;
  889. }
  890.  
  891. /* build a X-friendly widget name */
  892.  
  893. void
  894. build_name(name, first, second)
  895. char *name, *first, *second;
  896. {
  897.     char *ch;
  898.  
  899.     strcpy(name, first);
  900.     strcat(name, second);
  901.     for (ch = name; *ch; ch++) {
  902.     if (!isalnum(*ch))
  903.       *ch = '_';
  904.     }
  905. }
  906.  
  907. void
  908. do_help(w)
  909. Widget w;
  910. {
  911.     Widget w_form, w_done, w_text;
  912.     
  913.     if (!help_shell) {
  914.     /* first time: create help popup */
  915.     help_shell =
  916.       XtVaCreatePopupShell("help",
  917.                    topLevelShellWidgetClass,
  918.                    toplevel, NULL);
  919.     w_form =
  920.       XtVaCreateManagedWidget("helpForm", formWidgetClass,
  921.                   help_shell, NULL);
  922.     w_text =
  923.       XtVaCreateManagedWidget("helpText", labelWidgetClass, w_form,
  924.                   NULL);
  925.     w_done =
  926.       XtVaCreateManagedWidget("helpDone", commandWidgetClass, w_form,
  927.                   XtNfromVert, w_text,  NULL);
  928.     XtAddCallback(w_done, XtNcallback, done_help, NULL);
  929.     }
  930.     XtPopup(help_shell, XtGrabNone);
  931.     XMapRaised(XtDisplay(help_shell), XtWindow(help_shell));
  932. }
  933.  
  934. void
  935. done_help(w)
  936. Widget w;
  937. {
  938.     XtPopdown(help_shell);
  939. }
  940.  
  941. void
  942. do_select(w)
  943. Widget w;
  944. {
  945.     int i;
  946.  
  947.     for (i = 0; i < numfamilies; i++) {
  948.     XtVaSetValues(family[i].name, XtNstate, (Boolean) 1, NULL);
  949.     }
  950. }
  951.  
  952. void
  953. do_deselect(w)
  954. Widget w;
  955. {
  956.     int i;
  957.  
  958.     for (i = 0; i < numfamilies; i++) {
  959.     XtVaSetValues(family[i].name, XtNstate, (Boolean) 0, NULL);
  960.     }
  961. }
  962.  
  963. void
  964. do_toggle(w)
  965. Widget w;
  966. {
  967.     int i;
  968.     Boolean state;
  969.  
  970.     for (i = 0; i < numfamilies; i++) {
  971.     XtVaGetValues(family[i].name, XtNstate, &state, NULL);
  972.     XtVaSetValues(family[i].name, XtNstate, (Boolean) !state, NULL);
  973.     }
  974. }
  975.  
  976. void
  977. do_read(w)
  978. Widget w;
  979. {
  980.     FILE *stream = NULL;
  981.     char *filename;
  982.     int i, changed;
  983.     
  984. #ifdef HAVE_SELFILE
  985.     stream = XsraSelFile(toplevel,         "Read from file:              ",
  986.              "Okay", "Cancel", "Error: can't open file   ",
  987.              read_suggest, "r", NULL, &filename);
  988. #endif
  989.     if (!stream)
  990.       return;
  991.     
  992.     read_suggest = filename;
  993.     fclose(stream);
  994.     if (read_any_file(filename, mark_changed)) {
  995.     changed = numimages > numfamilies;
  996.     for (i = 0; i < numimages; i++) {
  997.         changed = changed || family[i].changed;
  998.     }
  999.     if (changed) {
  1000.         hide_image_families();
  1001.         numfamilies = min(numimages,MAXFAMS);
  1002.         for (i = 0; i < numimages; i++) {
  1003.         undisplay_family(i);
  1004.         }
  1005.         show_image_families(FALSE);
  1006.     }
  1007.     sprintf(buffer, "successfully read \"%s\"", filename);
  1008.     } else {
  1009.     sprintf(buffer, "failed reading \"%s\"", filename);
  1010.     }
  1011.     XtVaSetValues(message, XtNlabel, buffer, NULL);
  1012. }
  1013.  
  1014. void
  1015. mark_changed(imf, dummy)
  1016. ImageFamily *imf;
  1017. int dummy;
  1018. {
  1019.   int i;
  1020.  
  1021.   for (i = 0; i < numimages; i++) {
  1022.     if (imf==images[i]) {
  1023.       family[i].changed = TRUE;
  1024.       return;
  1025.     }
  1026.   }
  1027. }
  1028.  
  1029. void
  1030. do_save(w)
  1031. Widget w;
  1032. {
  1033.     int i, n;
  1034.     Boolean state;
  1035.     FILE *stream=NULL;
  1036.     char *filename;
  1037.     
  1038.     n = 0;
  1039.     for (i = 0; i < numfamilies; i++) {
  1040.     XtVaGetValues(family[i].name, XtNstate, &state, NULL);
  1041.     if (state) {
  1042.         if (!stream) {
  1043. #ifdef HAVE_SELFILE
  1044.         stream = XsraSelFile(toplevel,
  1045.                      "Save in file:              ",
  1046.                      "Okay", "Cancel",
  1047.                      "Error: can't open file   ",
  1048.                      write_suggest, "w", NULL, &filename);
  1049. #endif
  1050.         if (stream) {
  1051.             write_suggest = filename;
  1052.         } else {
  1053.             return;
  1054.         }
  1055.         }
  1056.         /* write_imf expects rawdata in "natural" order, not in the 
  1057.            X11 "byte-reversed" order the rest of this program uses */
  1058.         reverse_rawdata(images[i]);
  1059.         write_imf(stream, images[i]);
  1060.         reverse_rawdata(images[i]);
  1061.         n++;
  1062.     }
  1063.     }
  1064.     if (stream) {
  1065.     fclose(stream);
  1066.     if (n) {
  1067.         sprintf(buffer, "%d image families saved in \"%s\"",
  1068.             numfamilies, filename);
  1069.         XtVaSetValues(message, XtNlabel, buffer, NULL);
  1070.     }
  1071.     }
  1072. }
  1073.  
  1074. void
  1075. do_delete(w)
  1076. Widget w;
  1077. {
  1078.     delete_family(-1);
  1079. }
  1080.  
  1081. void
  1082. do_quit(w)
  1083. Widget w;
  1084. {
  1085.     exit(0);
  1086. }
  1087.  
  1088. /* callback: popup the family corresponding to the widget 
  1089.          do_open_family was called from */
  1090. void
  1091. do_open_family(w)
  1092. Widget w;
  1093. {
  1094.     int i;
  1095.  
  1096.     for (i = 0; i < numfamilies; i++) {
  1097.     if (w==family[i].image || w==family[i].name) {
  1098.         if (family[i].shell) {
  1099.         if (family[i].changed) {
  1100.             display_family(i, 0);
  1101.         }
  1102.         XMapRaised(XtDisplay(family[i].shell),
  1103.                XtWindow(family[i].shell));
  1104.         } else {
  1105.         display_family(i, 0);
  1106.         }
  1107.         return;
  1108.     }
  1109.     }
  1110. }
  1111.  
  1112. void
  1113. do_prep_delete(w)
  1114. Widget w;
  1115. {
  1116.     int i = shell_index(XtParent(XtParent(w)));
  1117.     FamilyStuff *fms = &family[i];
  1118.     
  1119.     fms->prep = prep_delete;
  1120.     
  1121.     set_cursor(fms, XCreateFontCursor(dpy, XC_pirate));
  1122.     XtVaSetValues(fms->delete, XtNleftBitmap, check, NULL);
  1123.     XtVaSetValues(fms->update, XtNleftBitmap, None, NULL);
  1124.     XtVaSetValues(fms->export, XtNleftBitmap, None, NULL);
  1125. }
  1126.  
  1127. void
  1128. do_prep_update(w)
  1129. Widget w;
  1130. {
  1131.     int i = shell_index(XtParent(XtParent(w)));
  1132.     FamilyStuff *fms = &family[i];
  1133.  
  1134.     fms->prep = prep_update;
  1135.     set_cursor(fms, XCreateFontCursor(dpy, XC_dot));
  1136.     XtVaSetValues(fms->update, XtNleftBitmap, check, NULL);
  1137.     XtVaSetValues(fms->delete, XtNleftBitmap, None, NULL);
  1138.     XtVaSetValues(fms->export, XtNleftBitmap, None, NULL);
  1139. }
  1140.  
  1141. void
  1142. do_prep_export(w)
  1143. Widget w;
  1144. {
  1145.     int i = shell_index(XtParent(XtParent(w)));
  1146.     FamilyStuff *fms = &family[i];
  1147.  
  1148.     fms->prep = prep_export;
  1149.     set_cursor(fms, XCreateFontCursor(dpy, XC_pencil));
  1150.     XtVaSetValues(fms->export, XtNleftBitmap, check, NULL);
  1151.     XtVaSetValues(fms->delete, XtNleftBitmap, None, NULL);
  1152.     XtVaSetValues(fms->update, XtNleftBitmap, None, NULL);
  1153. }
  1154.  
  1155. void
  1156. reset_prep (i)
  1157. int i;
  1158. {
  1159.     FamilyStuff *fms = &family[i];
  1160.  
  1161.     fms->prep = prep_none;
  1162.     set_cursor(fms, XCreateFontCursor(dpy, XC_left_ptr));
  1163.     XtVaSetValues(fms->delete, XtNleftBitmap, None, NULL);
  1164.     XtVaSetValues(fms->update, XtNleftBitmap, None, NULL);
  1165.     XtVaSetValues(fms->export, XtNleftBitmap, None, NULL);
  1166. }
  1167.  
  1168. void
  1169. set_cursor (fms, cursor)
  1170. FamilyStuff *fms;
  1171. Cursor cursor;
  1172. {
  1173.     ImageStuff *ims;
  1174.  
  1175.     for (ims = fms->images; ims; ims = ims->next) {
  1176.     if (ims->mono_w) {
  1177.         XtVaSetValues(ims->mono_w, XtNcursor, cursor, NULL);
  1178.     }
  1179.     if (ims->mask_w) {
  1180.         XtVaSetValues(ims->mask_w, XtNcursor, cursor, NULL);
  1181.     }
  1182.     if (ims->comb_w) {
  1183.         XtVaSetValues(ims->comb_w, XtNcursor, cursor, NULL);
  1184.     }
  1185.     if (ims->colr_w) {
  1186.         XtVaSetValues(ims->colr_w, XtNcursor, cursor, NULL);
  1187.     }
  1188.     }
  1189. }
  1190.  
  1191. void
  1192. do_close_family(w)
  1193. Widget w;
  1194. {
  1195.     int i = shell_index(XtParent(XtParent(w)));
  1196.  
  1197.     if (i >= numfamilies)
  1198.       return;
  1199.     undisplay_family(i);
  1200. }
  1201.  
  1202. void 
  1203. do_increase_magn(w)
  1204. Widget w;
  1205. {
  1206.     int i = shell_index(XtParent(XtParent(w)));
  1207.     int imag;
  1208.  
  1209.     if (i >= numfamilies)
  1210.       return;
  1211.  
  1212.     /* already at max? */
  1213.     if ((imag = family[i].imagn+1) >= NUMMAGN)
  1214.       return;
  1215.     undisplay_family(i);
  1216.     display_family(i, imag);
  1217. }
  1218.  
  1219. void 
  1220. do_decrease_magn(w)
  1221. Widget w;
  1222. {
  1223.     int i = shell_index(XtParent(XtParent(w)));
  1224.     int imag;
  1225.     
  1226.     /* already at min? */
  1227.     if ((imag = family[i].imagn-1) < 0)
  1228.       return;
  1229.     undisplay_family(i);
  1230.     display_family(i, imag);
  1231. }
  1232.  
  1233. int 
  1234. shell_index(w)
  1235. Widget w;
  1236. {
  1237.     int i;
  1238.  
  1239.     for (i = 0; i < numfamilies; i++) {
  1240.     if (family[i].shell == w)
  1241.       break;
  1242.     }
  1243.  
  1244.     return i;
  1245. }
  1246.  
  1247. Pixmap
  1248. magnify_bitmap (data, h, w, s)
  1249. char *data; 
  1250. int h, w, s;
  1251. {
  1252.     Pixmap pix;
  1253.     int lo, ln, i, j, is, js;
  1254.     char *new, *n, *o, *so, mo, mn;
  1255.     
  1256.     lo = (w + 7) / 8;
  1257.     ln = (w*s + 7) / 8;
  1258.     new = (char *) malloc(s * h* ln * sizeof(char));
  1259.     if (!new)
  1260.       return None;
  1261.     
  1262.     for (i = 0; i < s * h * ln; i++)
  1263.       new[i] = '\0';
  1264.     
  1265.     n = new-1;
  1266.     o = data-1;
  1267.     
  1268.     for (i = 0; i < h; i++) {
  1269.     so = o;
  1270.     for (is = 0; is < s; is++) {
  1271.         o = so;
  1272.         for (j = 0; j < w; j++) {
  1273.         if (!(j & 7)) {
  1274.             o++;
  1275.             mo = '\001';
  1276.         } else {
  1277.             mo <<= 1;
  1278.         }
  1279.         for (js = 0; js < s; js++) {
  1280.             if (!((s*j+js) & 7)) {
  1281.             n++;
  1282.             mn = '\001';
  1283.             } else {
  1284.             mn <<= 1;
  1285.             }
  1286.             if (*o & mo)
  1287.               *n |= mn;
  1288.         }
  1289.         }
  1290.     }
  1291.     }
  1292.     
  1293.     pix = XCreateBitmapFromData(dpy, rootwin, new, s*w, s*h);
  1294.     free(new); 
  1295.     return pix;      
  1296. }
  1297.  
  1298.  
  1299. Pixmap 
  1300. magnify_colrpix (img, s, bg_pix, mask)
  1301. Image *img;
  1302. int s;
  1303. Pixel bg_pix;
  1304. Pixmap mask;
  1305. {
  1306.     int i, j, r, ri, rc, c, l,
  1307.     rsize, rowbytesize, bytesize, rmask;
  1308.     char *dp, *rp, *rpsave, *data;
  1309.     Pixmap pixmap;
  1310.     Pixel index[256], pixel;
  1311.     XImage *ximage;
  1312.     GC gc;
  1313.     X11Image *ximg = (X11Image *) img->hook;
  1314.     
  1315.     if (!ximg || !img->rawcolrdata)
  1316.       return None;
  1317.     
  1318.     /* find reverse index->pixel mapping */
  1319.     for (c=0; c<256; c++) {
  1320.     index[c] = XBlackPixel(dpy,screen);
  1321.     }
  1322.     for (c=0; c<img->numcolors; c++) {
  1323.     index[img->rawpalette[4*c]] = ximg->colpix[c];
  1324.     }
  1325.     
  1326.     /* make color data */
  1327.     rsize = img->pixelsize;
  1328.     rmask = (1<<img->pixelsize) - 1;
  1329.     rowbytesize = img->w*s * depth/8;
  1330.     bytesize = rowbytesize * img->h*s;
  1331.     data = xmalloc(bytesize*sizeof(char));
  1332.     dp = data;
  1333.     rp = img->rawcolrdata;
  1334.     for (r=0; r<img->h; r++) {
  1335.     rpsave = rp;
  1336.     for (j=0; j<s; j++) {
  1337.         rp = rpsave;
  1338.         ri = 8 - img->pixelsize;
  1339.         for (c=0; c<img->w; c++) {
  1340.         rc = ((int) (*rp>>ri)) & rmask;
  1341.         if (ri) {
  1342.             ri -= img->pixelsize;
  1343.         } else {
  1344.             ri = 8 - img->pixelsize;
  1345.             rp++;
  1346.         }
  1347.         pixel = index[rc];
  1348.         for (i = 0; i<s; i++) {
  1349.             for (l=depth-8; l>=0; l-=8) {
  1350.             *dp = (pixel>>l) & 0xff;
  1351.             dp++;
  1352.             }
  1353.         }
  1354.         }
  1355.         if ((img->pixelsize*img->w)%8) {
  1356.         rp++;
  1357.         }
  1358.     }
  1359.     }
  1360.     
  1361.     /* convert to XImage */
  1362.     ximage = XCreateImage(dpy, DefaultVisual(dpy,screen), depth,
  1363.               ZPixmap, 0, data, img->w*s, img->h*s,
  1364.               8, rowbytesize);
  1365.     if (!ximage) {
  1366.     free(data);
  1367.     return None;
  1368.     }
  1369.     ximage->byte_order = MSBFirst;
  1370.     ximage->bitmap_bit_order = MSBFirst;
  1371.     
  1372.     /* and finally to Pixmap */
  1373.     pixmap = XCreatePixmap(dpy, rootwin, ximage->width, ximage->height, depth);
  1374.     if (!pixmap)  pixmap = None;
  1375.     if (pixmap == None) {
  1376.     XDestroyImage(ximage);
  1377.     /* XDestroyImage also frees data */
  1378.     return None;
  1379.     }
  1380.     gc = XCreateGC(dpy, pixmap, 0, NULL);
  1381.     
  1382.     /* background */
  1383.     XSetClipOrigin(dpy, gc, 0, 0);
  1384.     XSetForeground(dpy, gc, bg_pix);
  1385.     XFillRectangle(dpy, pixmap, gc, 0, 0, ximage->width, ximage->height);
  1386.     
  1387.     XSetClipMask(dpy, gc, mask);
  1388.     XPutImage(dpy, pixmap, gc, ximage, 0, 0, 0, 0,
  1389.           ximage->width, ximage->height);
  1390.     XFreeGC(dpy, gc);
  1391.     XDestroyImage(ximage);
  1392.     /* XDestroyImage also frees data */
  1393.     
  1394.     return pixmap;
  1395. }
  1396.  
  1397. /* actions */
  1398.  
  1399. void 
  1400. show_image (w, event, params, num_params)
  1401. Widget w;
  1402. XEvent *event;
  1403. String *params;
  1404. Cardinal *num_params;
  1405. {
  1406.     int i = shell_index(XtParent(XtParent(w)));
  1407.     FamilyStuff *fms = &family[i];
  1408.     ImageStuff *ims;
  1409.     char *add;
  1410.     
  1411.     sprintf(buffer, "%s: ", images[i]->name);
  1412.     if (fms->imagn) {
  1413.     sprintf(buffer+strlen(buffer)-2, " (x %d): ", magnif[fms->imagn]);
  1414.     }
  1415.     add = buffer + strlen(buffer);
  1416.     if (i>=numfamilies)  return;
  1417.     for (ims = fms->images; ims; ims = ims->next) {
  1418.     if (w == ims->mono_w) {
  1419.         sprintf(add, "%dx%d mono",  ims->w, ims->h);
  1420.     } else if (w == ims->mask_w) {
  1421.         sprintf(add, "%dx%d mask",  ims->w, ims->h);
  1422.     } else if (w == ims->comb_w) {
  1423.         sprintf(add, "%dx%d comb",  ims->w, ims->h);
  1424.     } else if (w == ims->colr_w) {
  1425.         sprintf(add, "%dx%d color", ims->w, ims->h);
  1426.     }
  1427.     }
  1428.     if (*add) {
  1429.     XtVaSetValues(fms->label, XtNlabel, buffer, NULL);
  1430.     }
  1431. }
  1432.  
  1433. void 
  1434. show_family (w, event, params, num_params)
  1435. Widget w;
  1436. XEvent *event;
  1437. String *params;
  1438. Cardinal *num_params;
  1439. {
  1440.     int i = shell_index(XtParent(XtParent(w)));
  1441.     FamilyStuff *fms = &family[i];
  1442.  
  1443.     if (fms->imagn) {
  1444.     sprintf(buffer, "%s (x %d)                   ",
  1445.         images[i]->name, magnif[fms->imagn]);
  1446.     } else {
  1447.     sprintf(buffer, "%s                   ",
  1448.         images[i]->name);
  1449.     }
  1450.     XtVaSetValues(fms->label, XtNlabel, buffer, NULL);
  1451. }
  1452.  
  1453. void 
  1454. image_action (w, event, params, num_params)
  1455. Widget w;
  1456. XEvent *event;
  1457. String *params;
  1458. Cardinal *num_params;
  1459. {
  1460.     int i = shell_index(XtParent(XtParent(w)));
  1461.     FamilyStuff *fms = &family[i];
  1462.     
  1463.     switch (event->type) {
  1464.       case ButtonPress:
  1465.     if (event->xbutton.button == 1) {
  1466.         if (fms->prep == prep_delete) {
  1467.         if (delete_image(w)) {
  1468.             if (empty_family(i)) {
  1469.             delete_family(i);
  1470.             return;
  1471.             } else {
  1472.             display_family(i, fms->imagn);
  1473.             }
  1474.         }
  1475.         } else if (fms->prep == prep_update) {
  1476.         update_or_export_image(w, 0);
  1477.         } else if (fms->prep == prep_export) {
  1478.         update_or_export_image(w, 1);
  1479.         }
  1480.         reset_prep(i);
  1481.     }
  1482.     }   
  1483. }
  1484.  
  1485. ImageStuff *
  1486. find_ims(fms, w)
  1487. FamilyStuff *fms;
  1488. Widget w;
  1489. {
  1490.     ImageStuff *ims;
  1491.  
  1492.     for (ims = fms->images; ims; ims = ims->next) {
  1493.     if (w == ims->mono_w) {
  1494.         return ims;
  1495.     } else if (w == ims->mask_w) {
  1496.         return ims;
  1497.     } else if (w == ims->comb_w) {
  1498.         return ims;
  1499.     } else if (w == ims->colr_w) {
  1500.         return ims;
  1501.     }
  1502.     }
  1503.     return NULL;
  1504. }
  1505.  
  1506. int
  1507. delete_image(w)
  1508. Widget w;
  1509. {
  1510.     int j, i;
  1511.     FamilyStuff *fms;
  1512.     ImageStuff *ims;
  1513.     Image *img;
  1514.     X11Image *ximg = NULL;
  1515.     
  1516.     if (!w)  return 0;
  1517.     i = shell_index(XtParent(XtParent(w)));
  1518.     fms = &family[i];
  1519.     ims = find_ims(fms,w);
  1520.     if (!ims)  return 0;
  1521.     img = find_image(images[i], ims->w, ims->h);
  1522.     if (!img)  return 0;
  1523.     ximg = (X11Image *) img->hook;
  1524.     if (!ximg)  return 0;
  1525.     
  1526.     if (w == ims->mono_w) {
  1527.     undisplay_family(i);
  1528.     /* do not free unmagnified pixmap; they might be used in the main window */
  1529.     ims->mono_p[0] = None;
  1530.     for (j=1; j<NUMMAGN; j++) {
  1531.         if (ims->mono_p[j] != None) {
  1532.         XFreePixmap(dpy, ims->mono_p[j]);
  1533.         ims->mono_p[j] = None;
  1534.         }
  1535.     }
  1536.     ximg->mono = None;
  1537.     ximg->monodata = NULL;
  1538.     img->rawmonodata = NULL;
  1539.     img->monodata = lispnil;
  1540.     } else if (w == ims->mask_w) {
  1541.     undisplay_family(i);
  1542.     ims->mask_p[0] = None;
  1543.     for (j=1; j<NUMMAGN; j++) {
  1544.         if (ims->mask_p[j] != None) {
  1545.         XFreePixmap(dpy, ims->mask_p[j]);
  1546.         ims->mask_p[j] = None;
  1547.         }
  1548.     }
  1549.     ximg->mask = None;
  1550.     ximg->maskdata = NULL;
  1551.     img->rawmaskdata = NULL;
  1552.     img->maskdata = lispnil;
  1553.     } else if (w == ims->colr_w) {
  1554.     undisplay_family(i);
  1555.     ims->colr_p[0] = None;
  1556.     for (j=1; j<NUMMAGN; j++) {
  1557.         if (ims->colr_p[j] != None) {
  1558.         XFreePixmap(dpy, ims->colr_p[j]);
  1559.         ims->colr_p[j] = None;
  1560.         }
  1561.     }
  1562.     ximg->colr = None;
  1563.     ximg->colrdata = NULL;
  1564.     img->rawcolrdata = NULL;
  1565.     img->colrdata = lispnil;
  1566.     } else if (w == ims->comb_w) {
  1567.     XBell(dpy,35);
  1568.     return 0;
  1569.     }
  1570.     if (w == ims->mono_w || w == ims->mask_w) {
  1571.     for (j=0; j<NUMMAGN; j++) {
  1572.         if (ims->comb_p[j] != None) {
  1573.         XFreePixmap(dpy, ims->comb_p[j]);
  1574.         ims->comb_p[j] = None;
  1575.         }
  1576.     }
  1577.     }
  1578.     
  1579.     return 1;
  1580. }
  1581.  
  1582. int
  1583. update_or_export_image (w, flag)
  1584. Widget w;
  1585. int flag;
  1586. {
  1587.     int i, ok = 0, imag;
  1588.     FamilyStuff *fms;
  1589.     ImageStuff *ims;
  1590.     Image *img;
  1591.     FILE *fp = NULL;
  1592.     ImageFamily *imf;
  1593.  
  1594.     if (!w)
  1595.       return 0;
  1596.     i = shell_index(XtParent(XtParent(w)));
  1597.     fms = &family[i];
  1598.     imf = images[i];
  1599.     ims = find_ims(fms, w);
  1600.     if (!ims)
  1601.       return 0;
  1602.     img = find_image(images[i], ims->w, ims->h);
  1603.     if (!img)
  1604.       return 0;
  1605.  
  1606.     if (outdirname && outdirname[0]) {
  1607.     sprintf(shortbuf, "%s/%s.%dx%d.",
  1608.         outdirname, imf->name, img->w, img->h);
  1609.     } else {
  1610.     sprintf(shortbuf, "%s.%dx%d.", imf->name, img->w, img->h);
  1611.     }
  1612.  
  1613.     if (w == ims->mono_w) {
  1614.     strcat(shortbuf, "b");
  1615.     if (flag) {
  1616.         if (img->rawmonodata && (fp = fopen(shortbuf, "w"))) {
  1617.         write_xbm_file(fp, imf->name, img->w, img->h, img->rawmonodata);
  1618.         ok = 1;
  1619.         }
  1620.     } else {
  1621.         if (read_xbm_file(shortbuf, imf, mark_changed)) {
  1622.         mark_changed(imf, 0);
  1623.         ok = 1;
  1624.         }
  1625.     }
  1626.     } else if (w == ims->mask_w) {
  1627.     strcat(shortbuf, "m");
  1628.     if (flag) {
  1629.         if (img->rawmaskdata && (fp = fopen(shortbuf, "w"))) {
  1630.         write_xbm_file(fp, imf->name, img->w, img->h, img->rawmaskdata);
  1631.         ok = 1;
  1632.         }
  1633.     } else {
  1634.         if (read_xbm_file(shortbuf, imf, mark_changed)) {
  1635.         mark_changed(imf, 0);
  1636.         ok = 1;
  1637.         }
  1638.     }
  1639.     } else  if (w == ims->colr_w) {
  1640.     strcat(shortbuf, "xpm");
  1641.     if (img->rawcolrdata && (fp = fopen(shortbuf, "w"))) {
  1642.         write_xpm_file(fp, imf->name, img);
  1643.         ok = 1;
  1644.     } else {
  1645.         if (read_xpm_file(shortbuf, imf, mark_changed)) {
  1646.         mark_changed(imf, 0);
  1647.         ok = 1;
  1648.         }
  1649.     }
  1650.     } else {
  1651.     XBell(dpy,35);
  1652.     return 0;
  1653.     }
  1654.  
  1655.     if (fp)
  1656.       fclose(fp);
  1657.  
  1658.     if (ok && !flag) {
  1659.     imag = fms->imagn;
  1660.     undisplay_family(i);
  1661.     x11_interp_imf(dpy, rootwin, images[i], TRUE);
  1662.     img = best_image(images[i], basew, baseh);
  1663.     if (img->hook != NULL) {
  1664.         X11Image *ximg = (X11Image *) img->hook;
  1665.         if (ximg->colr != None) {
  1666.         XtVaSetValues(fms->image, XtNbitmap, ximg->colr, NULL);
  1667.         } else if (ximg->mono != None) {
  1668.         XtVaSetValues(fms->image, XtNbitmap, ximg->mono, NULL);
  1669.         } else {
  1670.         XtVaSetValues(fms->image, XtNbitmap, ximg->mask, NULL);
  1671.         }
  1672.     }
  1673.     display_family(i, imag);
  1674.     }
  1675.  
  1676.     if (ok) {
  1677.     sprintf(buffer, "%s \"%s\"", flag ? "exported in" : "updated from",
  1678.         shortbuf);
  1679.     } else {
  1680.     XBell(dpy,35);
  1681.     sprintf(buffer, "%s failed", flag ? "export" : "update");
  1682.     }
  1683.     XtVaSetValues(fms->label, XtNlabel, buffer, NULL);
  1684.     return ok;
  1685. }
  1686.  
  1687. Image *
  1688. find_image(imf, w, h)
  1689. ImageFamily *imf;
  1690. int w, h;
  1691. {
  1692.     Image *img;
  1693.  
  1694.     for (img = imf->images; img != NULL; img = img->next) {
  1695.     if (w == img->w && h == img->h) {
  1696.         return img;
  1697.     }
  1698.     }
  1699.     return NULL;
  1700. }
  1701.  
  1702. /* i>=0: delete family #i;  i<0: delete selected families */
  1703.  
  1704. void 
  1705. delete_family (i)
  1706. int i;
  1707. {
  1708.     int j, n, hidden;
  1709.     Boolean state;
  1710.     
  1711.     hidden = 0;
  1712.     n = 0;
  1713.     for (j=0; j<numfamilies; j++) {
  1714.     if (i<0) {
  1715.         XtVaGetValues(family[j].name, XtNstate, &state, NULL);
  1716.     }
  1717.     if ((i<0 && state) || (j==i)) {
  1718.         if (!hidden)  hide_image_families();
  1719.         hidden = 1;
  1720.         destroy_family(j);
  1721.     } else {
  1722.         family[n] = family[j];
  1723.         images[n] = images[j];
  1724.         n++;
  1725.     }
  1726.     }
  1727.     
  1728.     if (numfamilies>n) {
  1729.     sprintf(buffer, "%d image families left", n);
  1730.     XtVaSetValues(message, XtNlabel, buffer, NULL);
  1731.     }
  1732.     numfamilies = numimages = n;
  1733.     if (hidden) {
  1734.     cols = MINCOLS;
  1735.     show_image_families(FALSE);
  1736.     }
  1737. }
  1738.  
  1739. void 
  1740. destroy_family (i)
  1741. int i;
  1742. {
  1743.     FamilyStuff *fms = &family[i];
  1744.     ImageStuff *ims;
  1745.  
  1746.     for (ims = fms->images; ims; ims = ims->next) {
  1747.     delete_image(ims->mono_w);
  1748.     delete_image(ims->mask_w);
  1749.     delete_image(ims->colr_w);
  1750.     }
  1751.     family[i].shell = NULL;
  1752. }
  1753.  
  1754. int
  1755. empty_family(i)
  1756. int i;
  1757. {
  1758.     Image *img;
  1759.     X11Image *ximg;
  1760.  
  1761.     for (img = images[i]->images; img != NULL; img = img->next) {
  1762.     if (!img->hook)
  1763.       continue;
  1764.     ximg = (X11Image *) img->hook;
  1765.     if (ximg->mono != None || ximg->colr != None || ximg->mask != None)
  1766.       return 0;
  1767.     }
  1768.     return 1;
  1769. }
  1770.